package com.utils;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
import java.util.Hashtable;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

/**
 * @author Nguyen Thien Phuong
 * 
 */
public class HtmlReader {
	
	private DataInputStream in = null;
	private DataOutputStream out = null;

	// e.g: "HTTP/1.1 200 OK"
	private String requestType = null;
	// key in lowercase
	private Hashtable headers = null;
	// http body
	private byte[] content = null;

	public HtmlReader() {
		this.headers = new Hashtable();
	}

	// ---------------------------------------------------
	public String getRequestType() {
		return requestType;
	}

	public Hashtable getHeaders() {
		return headers;
	}

	public byte[] getContent() {
		return content;
	}
	
	public String getContentString() {
		StringBuffer sb = new StringBuffer();
		if (content != null) {
			for (int i = 0; i < content.length; i++) {
				sb.append((char)content[i]);
			}
		}
		return sb.toString();
	}
	
	public String getStringContent() {
		StringBuffer sb = new StringBuffer();
		if (content != null) {
			for (int i = 0; i < content.length; i++) {
				sb.append(new String(new byte[]{content[i]}));
			}
		}
		return sb.toString();
	}

	public String getHeader(String name) {
		String key = (String) headers.get(name);
		return key;
	}

	// ---------------------------------------------------

	// Getting data from URL: http://server:port/uri/...
	public void doGet(String server, String uri, int port) {
		try {
			// Socket client = new Socket("localhost", 80);
			Socket client = new Socket(server, port);

			// client.setKeepAlive(true);
			this.in = new DataInputStream(client.getInputStream());
			this.out = new DataOutputStream(client.getOutputStream());

			// out.writeBytes("GET /vn/logo/top_logo.jsp HTTP/1.0\r\n");
			// --> http://localhost/vn/xxx
			out.writeBytes("GET " + uri + " HTTP/1.0\r\n");

			/* Optional Parameters */
			out.writeBytes("Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n");
			out.writeBytes("Accept-Language: en-us\r\n");
			out.writeBytes("Accept-Encoding: gzip, deflate\r\n");
			out.writeBytes("Host: " + server + "\r\n");
			out.writeBytes("User-Agent: Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n");
			out.writeBytes("Connection: Keep-Alive\r\n");

			/* This Header is very important and can't be ignored */
			out.writeBytes("Content-Length: 0\r\n\r\n");
			out.flush();

			/*******************************************************************
			 * int ch; while ( (ch = in.read()) >= 0) { System.out.print( (char)
			 * ch); } //
			 ******************************************************************/

			// /////////////////////////////
			parseRequest();
			// /////////////////////////////

			out.close();
			in.close();
			client.close();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}

	// ==========================================================================
	// ==========================================================================
	// From the input stream we extract the message and all the headers
	// (including Nokia HTTP extension headers)
	// Sequence:
	// 1. Request-Type =
	// POST /uri HTTP/1.1
	// GET /uri HTTP/1.1
	// 2. HTML Headers:
	// Content-Type: application/vnd.wap.mms-message
	// Content-Length: 22489
	// User-Agent: Java/1.4.1
	// Host: localhost:7000
	// Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
	// Connection: keep-alive
	//
	// 3. MM Content (if POST request):...
	// IN : this.in
	// --------------------------------------------------------------------------
	private boolean parseRequest() {
		byte[] match = { 13, 10, 13, 10 };

		int pos, ch;
		int index = 0;
		String line = "";
		try {
			while ((ch = in.read()) >= 0) {

				// The HTTP headers and the message body are separated by
				// blank line (a sequence of CR>LF>). In hex mode,
				// this shows as a sequence '0D0A0D0A' or {13, 10, 13, 10}
				if (ch == match[index] && index <= 3) {
					index++;
				} else {
					index = 0;
				}
				if (index == 4) {
					content = readHTTPContent();
					break;
				}

				// 2. Gets the request headers
				line += (char) ch;
				if ((line.length() > 2) && (ch == '\n')) {
					pos = line.indexOf(':');
					if (pos != -1) {
						// <header-name>:<header-value>\n
						String name = line.substring(0, pos); // .toLowerCase();
						String value = line.substring(pos + 1, line.length()).trim();
						this.setHeader(name, value);
					} else {
						// 1. Request Type
						// e.g: GET / HTTP/1.1 or GET /mmsc HTTP/1.1
						// <-> http://server/ or http://server/mmsc
						this.setRequestType(line.substring(0, line.length()).trim());
					}
					line = "";
				}
			}
			return true;
		} catch (Exception e) {
			System.out.println(e.getMessage());
			return false;
		}
	}

	// --------------------------------------------------------------------------
	// Store and print header to screen
	private void setHeader(String key, String value) {
		this.headers.put(key.toLowerCase(), value);
	}

	// --------------------------------------------------------------------------
	private byte[] readHTTPContent() throws IOException {
		ByteArrayOutputStream baosContent = new ByteArrayOutputStream();
		int contentLength = 0;
		try {
			contentLength = Integer.parseInt((String) headers.get("content-length"));
		} catch (Exception ex) {
			contentLength = 1024 * 1024;
		}

		int bytesToRead = 0;
		int bytesRead = 0;
		int totalBytesRead = 0;
		int bufferSize = 1024;
		byte[] buffer = new byte[bufferSize];

		if (contentLength < bufferSize) {
			bytesToRead = contentLength;
		} else {
			bytesToRead = bufferSize;
		}
		do {
			try {
				bytesRead = in.read(buffer, 0, bytesToRead);
			} catch (InterruptedIOException e) {
				/* comms read timeout expired, no problem */
				System.out.println("Timeout reading from socket");
			}
			if (bytesRead == -1) {
				in.close();
				// throw new IOException("Connection was closed by client.");
				break;
			} else if (bytesRead > 0) {
				// ////////////////////////////////////
				baosContent.write(buffer, 0, bytesRead);
				// ////////////////////////////////////
				totalBytesRead += bytesRead;
			}
			// Left bytes to read
			if (contentLength - totalBytesRead > bufferSize) {
				bytesToRead = bufferSize;
			} else {
				bytesToRead = contentLength - totalBytesRead;
			}
		} while (totalBytesRead < contentLength);

		// /////////////////////////////////////
		return baosContent.toByteArray();
		// /////////////////////////////////////
	}

	private void setRequestType(String s) {
		this.requestType = new String(s);
	}
//	
//	/**
//	 * Read content of web page by htmlparser
//	 */
//
	public static NodeList extractAllNodesThatMatch(String strUrl,
			String tagNameFilter, String attributeNameFilter,
			String attributeValueFilter) throws IOException, ParserException {
		NodeList nodeList = null;
		URL url = new URL(strUrl);
		HttpURLConnection urlConnection = (HttpURLConnection) url
				.openConnection();
		urlConnection.setDoInput(true);
		urlConnection.setDoOutput(true);
		urlConnection.setRequestMethod("GET");
		urlConnection.setConnectTimeout(30000); // 30 second timeout
		Parser parser = new Parser(urlConnection);
		NodeFilter nodeFilter = new AndFilter(new TagNameFilter(tagNameFilter),
				new HasAttributeFilter(attributeNameFilter,
						attributeValueFilter));
		parser.setEncoding("utf-8");
		nodeList = parser.extractAllNodesThatMatch(nodeFilter);
		return nodeList;
	}
	
	public String parseInfo(String elementName) {
//		<?xml version="1.0" encoding="utf-8"?>
//		<bstc_status>
//			<bstc_statusmsg>0</bstc_statusmsg>
//		</bstc_status>
		String info = getContentString();
		StringReader rd = new StringReader(info);
		InputSource is = new InputSource(rd);
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = null;
			try {
				builder = factory.newDocumentBuilder();
			} catch (ParserConfigurationException e) {
				e.printStackTrace();
				return null;
			}
			Document document = null;
			document = builder.parse(is);
			org.w3c.dom.NodeList list = document.getFirstChild().getChildNodes();

			for (int i = 0; i < list.getLength(); i++) {
				if (list.item(i) == null) continue;
				Node firstChildNode = list.item(i).getFirstChild();
				if (firstChildNode != null && elementName.equalsIgnoreCase(list.item(i).getNodeName()))
					return firstChildNode.getNodeValue();
			}

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return null;
	}
	
	public static void main(String[] args) {
		try {
			HtmlReader reader = new HtmlReader();
			reader.doGet("vnexpress.net", "/GL/Home/", 80);
			System.out.println(reader.getStringContent());
		} catch (Exception e) {
		}
	}
}
